package user

import (
	"encoding/json"
	"fleetmanager/api/common/log"
	"fleetmanager/api/errors"
	"fleetmanager/api/model/user"
	"fleetmanager/api/response"
	userserver "fleetmanager/api/service/user"
	"fleetmanager/api/validator"
	"fleetmanager/client"
	"fleetmanager/db/dao"
	"fleetmanager/logger"
	"fmt"
	"github.com/beego/beego/v2/client/orm"
	"github.com/google/uuid"
	"net/http"
	"strings"
)

type ReturnResConf struct {
	Total       int                       `json:"Total"`
	ResConfList []user.UserResourceConfig `json:"ResConfList"`
}

func (c *UserController) BindResourceConfig() {
	tLogger := log.GetTraceLogger(c.Ctx).WithField(logger.Stage, "user resource config")
	body := c.Ctx.Input.RequestBody
	resConf := user.BindResourceConfig{}
	if err := json.Unmarshal(body, &resConf); err != nil {
		tLogger.Error("Json unmarshal err: %+v", err.Error())
		response.InputError(c.Ctx)
		return
	}
	if err := validator.Validate(&resConf); err != nil {
		tLogger.Error("validate resource conf err:%+v", err.Error())
		response.Error(c.Ctx, http.StatusBadRequest,
			errors.NewErrorF(errors.InvalidUserinfo, err.Error()))
		return
	}
	newConf, err := bindExistUser(resConf)
	if err != nil {
		tLogger.Error("bind exist user err:%+v", err)
		response.Error(c.Ctx, http.StatusInternalServerError,
			errors.NewErrorF(errors.OperateResConfigFailed, err.Error()))
		return
	}
	if err := c.addAgencyToAASS(*newConf); err != nil {
		tLogger.Error("add agency info to aass failed for %+v, event maybe failed", err.Error())
	}
	// 更新租户数量
	if err := updateTotaResNum(tLogger, resConf.UserId); err != nil {
		tLogger.Error("update total num info err: %+v", err.Error())
		response.Error(c.Ctx, http.StatusInternalServerError,
			errors.NewErrorF(errors.DBError, "update userinfo failed"))
		return
	}
	response.Success(c.Ctx, http.StatusCreated, resConf)
}

func (c *UserController) InsertResourceConfig() {
	tLogger := log.GetTraceLogger(c.Ctx).WithField(logger.Stage, "user resource config")
	to, err := orm.NewOrm().Begin()
	if err != nil {
		tLogger.Error("Orm Init err: %+v", err.Error())
		response.Error(c.Ctx, http.StatusInternalServerError, errors.NewError(errors.ServerInternalError))
		return
	}
	resConf := user.UserResourceConfig{}
	body := c.Ctx.Input.RequestBody
	if err := json.Unmarshal(body, &resConf); err != nil {
		tLogger.Error("Json unmarshal err: %+v", err.Error())
		response.InputError(c.Ctx)
		return
	}
	if err := validator.Validate(&resConf); err != nil {
		tLogger.Error("validate resource conf err:%+v", err.Error())
		response.Error(c.Ctx, http.StatusBadRequest,
			errors.NewErrorF(errors.InvalidUserinfo, err.Error()))
		return
	}
	if err := checkUserInfo(&resConf); err != nil {
		tLogger.Error("check user info failed for %+v", err.Error())
		response.Error(c.Ctx, http.StatusBadRequest,
			errors.NewErrorF(errors.InvalidUserinfo, err.Error()))
		return
	}
	if err := InsertRes(to, &resConf); err != nil {
		to.Rollback()
		tLogger.Error("Insert resource %+v, request: %s", err.Error(), body)
		response.Error(c.Ctx, http.StatusInternalServerError,
			errors.NewErrorF(errors.OperateResConfigFailed, err.Error()))
		return
	}
	if err := to.Commit(); err != nil {
		tLogger.Error("Commit err: %+v", err.Error())
		response.Error(c.Ctx, http.StatusInternalServerError, errors.NewError(errors.ServerInternalError))
		return
	}
	// 将租户信息同步至AASS，失败不阻塞，创建Fleet仍会进行同步，在此之前事件功能可能会异常
	if err := c.addAgencyToAASS(resConf); err != nil {
		tLogger.Error("add agency info to aass failed for %+v, event maybe failed", err.Error())
	}
	// 更新租户数量
	if err := updateTotaResNum(tLogger, resConf.UserId); err != nil {
		tLogger.Error("update total num info err: %+v", err.Error())
		response.Error(c.Ctx, http.StatusInternalServerError,
			errors.NewErrorF(errors.DBError, "update userinfo failed"))
		return
	}
	response.Success(c.Ctx, http.StatusCreated, resConf)
}

func (c *UserController) addAgencyToAASS(conf user.UserResourceConfig) error {
	body, err := json.Marshal(conf)
	if err != nil {
		return err
	}

	// do service call
	newReq := client.NewRequest(client.ServiceNameAASS, c.getUrl(conf.Region), http.MethodPost, body)
	code, rsp, err := newReq.DoRequest()
	if err != nil {
		return err
	}
	if code < http.StatusOK || code > http.StatusBadRequest {
		return fmt.Errorf("response code %d is not the success code and rsp is %+v", code, rsp)
	}
	return nil
}

func (c *UserController) UpdateResourceConfig() {
	tLogger := log.GetTraceLogger(c.Ctx).WithField(logger.Stage, "user resource config")
	to, err := orm.NewOrm().Begin()
	if err != nil {
		tLogger.Error("Orm Init err: %+v", err.Error())
		response.Error(c.Ctx, http.StatusInternalServerError, errors.ServerInternalError)
		return
	}
	resConf := user.UpdateUserResConfig{}
	body := c.Ctx.Input.RequestBody
	if err := json.Unmarshal(body, &resConf); err != nil {
		tLogger.Error("Json unmarshal err: %+v", err.Error())
		response.InputError(c.Ctx)
		return
	}
	// 校验租户信息
	if err := validator.Validate(&resConf); err != nil {
		tLogger.Error("validate resource conf err:%+v", err.Error())
		response.Error(c.Ctx, http.StatusBadRequest,
			errors.NewErrorF(errors.InvalidUserinfo, err.Error()))
		return
	}

	if err := UpdateRes(to, body, resConf.Id); err != nil {
		tLogger.Error("Update resource %+v, request: %s", err.Error(), body)
		response.Error(c.Ctx, http.StatusInternalServerError,
			errors.NewErrorF(errors.OperateResConfigFailed, err.Error()))
		return
	}
	if err := to.Commit(); err != nil {
		tLogger.Error("Commit err: %+v", err.Error())
		response.Error(c.Ctx, http.StatusInternalServerError, errors.NewError(errors.ServerInternalError))
		return
	}
	response.Success(c.Ctx, http.StatusOK, resConf)
}

func (c *UserController) DeleteResConfig() {
	tLogger := log.GetTraceLogger(c.Ctx).WithField(logger.Stage, "user resource config")
	to, err := orm.NewOrm().Begin()
	if err != nil {
		tLogger.Error("Orm Init err: %+v", err.Error())
		response.Error(c.Ctx, http.StatusInternalServerError, errors.ServerInternalError)
		return
	}
	id := c.GetString("id")
	resConf, err := dao.GetUserResConfById(id)
	if err != nil {
		tLogger.Error("get user resource config err: %+v, resource id: %s", err, id)
		response.Error(c.Ctx, http.StatusInternalServerError,
			errors.NewErrorF(errors.OperateResConfigFailed, "Get user resource config failed"))
		return
	}
	userid := resConf.Userid
	err = DeleteRes(to, id)
	if err != nil {
		tLogger.Error("Delete resource %+v, config id: %s", err.Error(), id)
		response.Error(c.Ctx, http.StatusInternalServerError,
			errors.NewErrorF(errors.OperateResConfigFailed, "Delete resource failed"))
		return
	}
	if err := to.Commit(); err != nil {
		tLogger.Error("Commit err: %+v", err.Error())
		response.Error(c.Ctx, http.StatusInternalServerError,
			errors.NewError(errors.ServerInternalError))
		return
	}
	// 租户数量-1
	err = updateTotaResNum(tLogger, userid)
	if err != nil {
		tLogger.Error("update total num info err: %+v", err.Error())
		response.Error(c.Ctx, http.StatusInternalServerError,
			errors.NewErrorF(errors.DBError, "Update userinfo failed"))
	}
	response.Success(c.Ctx, http.StatusNoContent, errors.NewErrorF(errors.NoError, "Delete success"))
}

// 获取用户的所有租户信息
func (c *UserController) GetResourceConfig() {
	tLogger := log.GetTraceLogger(c.Ctx).WithField(logger.Stage, "user get resource config")
	authId := parseTokenToUserId(c.Ctx)
	queryId := c.GetString("id")
	service := userserver.NewUserService(c.Ctx, tLogger)
	resConfList, err := service.GetResourceConfig(authId, queryId, tLogger)
	if err != nil {
		tLogger.Error("get resconf err:%s", err.Error())
		response.Error(c.Ctx, http.StatusInternalServerError,
			errors.NewErrorF(errors.ServerInternalError, err.Error()))
		return
	}

	tLogger.Info("get user resource config success")
	response.Success(c.Ctx, http.StatusOK, resConfList)
}

// 管理员获取所有租户信息
func (c *UserController) GetAllResourceConfig() {
	tLogger := log.GetTraceLogger(c.Ctx).WithField(logger.Stage, "admin get resource config")
	service := userserver.NewUserService(c.Ctx, tLogger)
	ReturnResConf, err := service.ListResConf()
	if err != nil {
		tLogger.Error("list all resource config error:%+v", err)
		response.Error(c.Ctx, http.StatusInternalServerError,
			errors.NewErrorF(errors.ServerInternalError, err.Error()))
	}
	tLogger.Info("list user resource config success")
	response.Success(c.Ctx, http.StatusOK, ReturnResConf)
}

// 获取单条resource
func (c *UserController) GetOneResourceConfig() {
	tLogger := log.GetTraceLogger(c.Ctx).WithField(logger.Stage, "get one resource config")
	id := c.GetString("id")
	if id == "" {
		tLogger.Error("GetUserResConfById err:%+v", "resource id is empty")
		response.Error(c.Ctx, http.StatusBadRequest,
			errors.NewErrorF(errors.InvalidParameterValue, "resource id is empty"))
		return
	}
	resConf := user.UserResourceConfig{}
	userconf, err := dao.GetUserResConfById(id)
	if err != nil {
		tLogger.Error("GetUserResConfById err:%+v", err.Error())
		response.Error(c.Ctx, http.StatusInternalServerError,
			errors.NewError(errors.ServerInternalError))
		return
	}
	err = GetResConfigHandler(&resConf, userconf)
	if err != nil {
		tLogger.Error("GetResConfigHandler err:%+v", err.Error())
		response.Error(c.Ctx, http.StatusInternalServerError,
			errors.NewError(errors.ServerInternalError))
		return
	}
	tLogger.Info("get user resource config %s success", id)
	response.Success(c.Ctx, http.StatusOK, resConf)
}

func getResConfig(userId string, tLogger *logger.FMLogger) (int, []user.UserResourceConfig, error) {
	userConfList, err := dao.GetAllResConfig(userId)
	if err != nil {
		return 0, nil, err
	}
	var resConfList []user.UserResourceConfig
	// 循环访问user信息
	for _, userConf := range userConfList {
		resConf := user.UserResourceConfig{}
		err = GetResConfigHandler(&resConf, &userConf)
		if err != nil {
			tLogger.Error("get resource config err:%s", err.Error())
			continue
		}
		resConfList = append(resConfList, resConf)
	}
	total := len(resConfList)
	return total, resConfList, nil
}
func GetResConfigHandler(resConf *user.UserResourceConfig, userConf *dao.UserResConf) error {
	resConf.OriProjectId = userConf.OriProjectId
	resConf.UserId = userConf.Userid
	resConf.Username = userConf.Username
	resConf.ProjectId = userConf.ProjectId
	resConf.Region = userConf.Region
	resConf.KeypairName = userConf.KeyPairName
	resConf.AccessKey = userConf.AccessKey
	resConf.SecretAccessKey = userConf.SecretAccessKey
	resConf.DomainName = userConf.DomainName
	resConf.AgencyName = userConf.AgencyName
	resConf.BuildScriptPath = userConf.BuildScriptPath
	resConf.AuxProxyPath = userConf.AuxProxyPath
	resConf.FileStorageRegion = userConf.FileStorageRegion
	resConf.DockerBuildScriptPath = userConf.DockerBuildScriptPath
	resConf.Id = userConf.Id
	resConf.Username = userConf.Username
	resConf.UserId = userConf.Id
	return nil
}

func DeleteRes(to orm.TxOrmer, indexId string) error {
	if err := dao.DeleteUserResConfById(to, indexId); err != nil {
		return fmt.Errorf("resuserconf err:%+v", err.Error())
	}
	return nil
}

func InsertRes(to orm.TxOrmer, resConf *user.UserResourceConfig) error {
	u, err := uuid.NewUUID()
	if err != nil {
		return fmt.Errorf("get uuid err %+v", err)
	}
	id := u.String()
	id = strings.ReplaceAll(id, "-", "")
	resConf.OriProjectId = id
	if err := dao.InsertUserResConfByJson(to, id, resConf); err != nil {
		return fmt.Errorf("resuserconf err :%+v", err.Error())
	}
	return nil
}

func UpdateRes(to orm.TxOrmer, body []byte, indexId string) error {
	// 在update user_res_conf中做去重验证
	if err := dao.UpdateUserResConfByJson(to, body, indexId); err != nil {
		return fmt.Errorf("res user res conf err :%+v", err.Error())
	}
	return nil
}

// 统计并更新用户的租户信息数量
func updateTotaResNum(tLogger *logger.FMLogger, userid string) error {
	count, err := dao.GetUserResConf().Count(dao.Filters{"UserId": userid})
	if err != nil {
		tLogger.Error("get res info err: %+v", err.Error())
		return err
	}
	userinfo, err := dao.GetUser().Get(dao.Filters{"Id": userid})
	if err != nil {
		tLogger.Error("get userinfo info err: %+v", err.Error())
		return err
	}
	userinfo.TotalResNumber = int(count)
	err = dao.GetUser().Update(userinfo, "total_res_number")
	if err != nil {
		tLogger.Error("update userinfo info err: %+v", err.Error())
		return err
	}
	return nil
}

func bindExistUser(bindConf user.BindResourceConfig) (*user.UserResourceConfig, error) {
	resConf := user.UserResourceConfig{}
	userResConf, err := dao.GetOriginUserResConfByOriProjectId(bindConf.OriProjectId)
	if err != nil {
		return &resConf, fmt.Errorf("get user resconf by id %s error for %v", bindConf.OriProjectId, err)
	}

	errB := GetResConfigHandler(&resConf, userResConf)
	if errB != nil {
		return &resConf, fmt.Errorf("convert resconfig failed for %v", errB)
	}
	resConf.DomainName = bindConf.DomainName
	resConf.UserId = bindConf.UserId
	resConf.AgencyName = userResConf.AgencyName
	resConf.AccessKey = userResConf.AccessKey
	resConf.SecretAccessKey = userResConf.SecretAccessKey

	to, errC := orm.NewOrm().Begin()
	if errC != nil {
		return &resConf, fmt.Errorf("init orm failed for %v", errC)
	}
	errD := dao.InsertUserResConfByJson(to, resConf.OriProjectId, &resConf)
	if errD != nil {
		return &resConf, fmt.Errorf("insert user resconf failed for %v", errD)
	}
	errE := to.Commit()
	if errE != nil {
		return &resConf, fmt.Errorf("resconf commit error for %v", errE)
	}
	return &resConf, nil
}
